#region License
// Copyright (c) Dmitry Antonenko (http://hystrix.com.ua)
// 
// Licensed under the Apache License, Version 2.0 (the "License"); 
// you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at 
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
// See the License for the specific language governing permissions and 
// limitations under the License.
// 
// The latest version of this file can be found at http://fluentfilters.codeplex.com/
#endregion

namespace FluentFilters
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;

    public class FluentFilterCollection : IFilterProvider
    {
        #region Fields

        private List<FilterRecord> _records = new List<FilterRecord>();

        #endregion

        #region Properties

        public int Count
        {
            get { return _records.Count; }
        }

        #endregion

        #region Public Methods

        public void Add<T>()
        {
            AddInternal<T>(order: null);
        }

        public void Add<T>(int order)
        {
            AddInternal<T>(order);
        }

        public void Add<T>(Action<IFilterCriteriaBuilder> criteria)
        {
            AddInternal<T>(criteria, order: null);
        }

        public void Add<T>(Action<IFilterCriteriaBuilder> criteria, int order)
        {
            AddInternal<T>(criteria, order);
        }

        public void Add(object filter, Action<IFilterCriteriaBuilder> criteria)
        {
            AddInternal(filter, criteria, null);
        }

        public void Add(object filter, Action<IFilterCriteriaBuilder> criteria, int order)
        {
            AddInternal(filter, criteria, order);
        }

        public void Clear()
        {
            _records.Clear();
        }

        public void Remove<T>()
        {
            _records.RemoveAll(f => f.FilterType.Equals(typeof(T)));
        }

        #endregion

        #region Private Methods

        private void AddInternal(object filter, Action<IFilterCriteriaBuilder> criteria, int? order)
        {
            var builder = new FilterCriteriaBuilder();
            criteria(builder);

            _records.Add(new FilterRecord(builder.GetResults(), filter, order));
        }

        private void AddInternal<T>(Action<IFilterCriteriaBuilder> criteria, int? order)
        {
            var builder = new FilterCriteriaBuilder();
            criteria(builder);

            _records.Add(new FilterRecord(builder.GetResults(), typeof(T), order));
        }

        private void AddInternal<T>(int? order)
        {
            _records.Add(new FilterRecord(Enumerable.Empty<FilterCriteriaResult>(), typeof(T), order));
        }

        #endregion

        #region Virtual Members

        protected virtual IMvcFilter GetFilterInstance(Type filterType)
        {
            return (IMvcFilter)Activator.CreateInstance(filterType);
        }

        #endregion

        #region Implementation of IFilterProvider

        IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            var filters = new List<Filter>();

            foreach (var record in _records)
            {
                if (record.Match(controllerContext, actionDescriptor))
                {
                    if (record.FilterInstance != null)
                    {
                        filters.Add(new Filter(record.FilterInstance, FilterScope.Global, record.Order));
                    }
                    else
                    {
                        filters.Add(new Filter(GetFilterInstance(record.FilterType), FilterScope.Global, record.Order));
                    }
                }
            }

            return filters;
        }

        #endregion
    }
}